﻿
Imports System.IO
Imports osi.root.delegates
Imports osi.root.constants
Imports osi.root.connector
Imports osi.root.lock
Imports osi.root.procedure
Imports osi.root.formation
Imports osi.root.utils
Imports osi.service.argument
Imports osi.service.convertor
Imports osi.service.device
Imports osi.service.selector

<global_init(global_init_level.server_services)>
Public Class responder
    Private ReadOnly continuous As Boolean
    Private ReadOnly instance As herald
    Private ReadOnly pool As new_device_event_isync_device_pool(Of herald)
    Private ReadOnly name As String
    Private ReadOnly pending_request_timeout_ms As Int64
    Private ReadOnly device_change_check_interval_ms As Int64
    Private ReadOnly e As executor
    Private ReadOnly stopping As Func(Of Boolean)

    Private Sub New(ByVal continuous As Boolean,
                    ByVal instance As herald,
                    ByVal pool As new_device_event_isync_device_pool(Of herald),
                    ByVal name As String,
                    ByVal pending_request_timeout_ms As Int64,
                    ByVal device_change_check_interval_ms As Int64,
                    ByVal e As executor,
                    ByVal stopping As Func(Of Boolean))
        assert((Not instance Is Nothing AndAlso pool Is Nothing AndAlso name Is Nothing) OrElse
               (instance Is Nothing AndAlso Not pool Is Nothing AndAlso name Is Nothing) OrElse
               (instance Is Nothing AndAlso pool Is Nothing AndAlso Not String.IsNullOrEmpty(name)))
        assert(continuous OrElse stopping Is Nothing)
        Me.continuous = continuous
        Me.instance = instance
        Me.pool = pool
        Me.name = name
        Me.pending_request_timeout_ms = pending_request_timeout_ms
        If device_change_check_interval_ms < 0 Then
            Me.device_change_check_interval_ms = constants.device_change_check_interval_ms
        Else
            Me.device_change_check_interval_ms = device_change_check_interval_ms
        End If
        Me.e = e
        Me.stopping = stopping
        If Not pool Is Nothing Then
            Me.stopping = Function() As Boolean
                              Return stopping.not_null_and_true() OrElse
                                     pool.expired()
                          End Function
        End If
    End Sub

    Public Sub New(ByVal continuous As Boolean,
                   ByVal instance As herald,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   Optional ByVal stopping As Func(Of Boolean) = Nothing)
        Me.New(continuous,
               instance,
               Nothing,
               Nothing,
               pending_request_timeout_ms,
               0,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal continuous As Boolean,
                   ByVal instance As herald,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   ByVal stopping As wrapper(Of singleentry))
        Me.New(continuous,
               instance,
               pending_request_timeout_ms,
               e,
               AddressOf stopping.not_null_and_in_use)
    End Sub

    Public Sub New(ByVal instance As herald,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   Optional ByVal stopping As Func(Of Boolean) = Nothing)
        Me.New(True,
               instance,
               pending_request_timeout_ms,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal instance As herald,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   ByVal stopping As wrapper(Of singleentry))
        Me.New(instance,
               pending_request_timeout_ms,
               e,
               AddressOf stopping.not_null_and_in_use)
    End Sub

    Public Sub New(ByVal continuous As Boolean,
                   ByVal pool As new_device_event_isync_device_pool(Of herald),
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   Optional ByVal stopping As Func(Of Boolean) = Nothing)
        Me.New(continuous,
               Nothing,
               pool,
               Nothing,
               pending_request_timeout_ms,
               0,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal continuous As Boolean,
                   ByVal pool As new_device_event_isync_device_pool(Of herald),
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   ByVal stopping As wrapper(Of singleentry))
        Me.New(continuous,
               pool,
               pending_request_timeout_ms,
               e,
               AddressOf stopping.not_null_and_in_use)
    End Sub

    Public Sub New(ByVal pool As new_device_event_isync_device_pool(Of herald),
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   Optional ByVal stopping As Func(Of Boolean) = Nothing)
        Me.New(True,
               pool,
               pending_request_timeout_ms,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal pool As new_device_event_isync_device_pool(Of herald),
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   ByVal stopping As wrapper(Of singleentry))
        Me.New(True,
               pool,
               pending_request_timeout_ms,
               e,
               AddressOf stopping.not_null_and_in_use)
    End Sub

    Public Sub New(ByVal continuous As Boolean,
                   ByVal name As String,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal device_change_check_interval_ms As Int64,
                   ByVal e As executor,
                   Optional ByVal stopping As Func(Of Boolean) = Nothing)
        Me.New(continuous,
               Nothing,
               Nothing,
               name,
               pending_request_timeout_ms,
               device_change_check_interval_ms,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal continuous As Boolean,
                   ByVal name As String,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   Optional ByVal stopping As Func(Of Boolean) = Nothing)
        Me.New(continuous,
               name,
               pending_request_timeout_ms,
               npos,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal continuous As Boolean,
                   ByVal name As String,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal device_change_check_interval_ms As Int64,
                   ByVal e As executor,
                   ByVal stopping As wrapper(Of singleentry))
        Me.New(continuous,
               name,
               pending_request_timeout_ms,
               device_change_check_interval_ms,
               e,
               AddressOf stopping.not_null_and_in_use)
    End Sub

    Public Sub New(ByVal continuous As Boolean,
                   ByVal name As String,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   ByVal stopping As wrapper(Of singleentry))
        Me.New(continuous,
               name,
               pending_request_timeout_ms,
               npos,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal name As String,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal device_change_check_interval_ms As Int64,
                   ByVal e As executor,
                   Optional ByVal stopping As Func(Of Boolean) = Nothing)
        Me.New(True,
               name,
               pending_request_timeout_ms,
               device_change_check_interval_ms,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal name As String,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   Optional ByVal stopping As Func(Of Boolean) = Nothing)
        Me.New(name,
               pending_request_timeout_ms,
               npos,
               e,
               stopping)
    End Sub

    Public Sub New(ByVal name As String,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal device_change_check_interval_ms As Int64,
                   ByVal e As executor,
                   ByVal stopping As wrapper(Of singleentry))
        Me.New(True,
               name,
               pending_request_timeout_ms,
               device_change_check_interval_ms,
               e,
               AddressOf stopping.not_null_and_in_use)
    End Sub

    Public Sub New(ByVal name As String,
                   ByVal pending_request_timeout_ms As Int64,
                   ByVal e As executor,
                   ByVal stopping As wrapper(Of singleentry))
        Me.New(name,
               pending_request_timeout_ms,
               npos,
               e,
               stopping)
    End Sub

    Public Function valid() As Boolean
        Return (Not instance Is Nothing OrElse
                Not pool Is Nothing OrElse
                Not String.IsNullOrEmpty(name)) AndAlso
               Not e Is Nothing
    End Function

    Private Function expired() As Boolean
        Return stopping.not_null_and_true()
    End Function

    Public Shared Operator -(ByVal this As responder) As Boolean
        If this Is Nothing Then
            Return False
        ElseIf Not this.pool Is Nothing Then
            Return this.respond_all()
        ElseIf Not String.IsNullOrEmpty(this.name) Then
            Return this.respond_name()
        Else
            Return False
        End If
    End Operator

    Private Function respond_all() As Boolean
        assert(Not pool Is Nothing)
        Return pool.get_all(AddressOf respond_one, stopping)
    End Function

    Private Function respond_name(ByVal name As String) As Boolean
        assert(Not String.IsNullOrEmpty(name))
        Dim ec As event_comb = Nothing
        Dim p As new_device_event_isync_device_pool(Of herald) = Nothing
        begin_lifetime_event_comb(AddressOf expired,
                                  Function() As Boolean
                                      Dim t As new_device_event_isync_device_pool(Of herald) = Nothing
                                      If device_manager.get(name, t) AndAlso
                                         object_compare(t, p) <> 0 Then
                                          p = t
                                          Return -(New responder(p,
                                                                 pending_request_timeout_ms,
                                                                 e,
                                                                 stopping))
                                      Else
                                          Return waitfor(device_change_check_interval_ms)
                                      End If
                                  End Function)
        Return True
    End Function

    Private Function respond_name() As Boolean
        Return respond_name(name)
    End Function

    Public Shared Operator +(ByVal this As responder) As event_comb
        If this Is Nothing OrElse
           this.instance Is Nothing Then
            Return Nothing
        Else
            Return this.respond_one()
        End If
    End Operator

    Public Function respond_one_of() As Boolean
        assert(Not pool Is Nothing)
        assert(Not stopping Is Nothing)
        Dim ec As event_comb = Nothing
        Return begin(New event_comb(Function() As Boolean
                                        If stopping() Then
                                            Return goto_end()
                                        Else
                                            Dim h As herald = Nothing
                                            If pool.sync_get(h) Then
                                                ec = respond_one(h)
                                                'TODO: log?
                                                Return waitfor(ec)
                                            Else
                                                Return waitfor(constants.respond_one_of_wait_device_ms)
                                            End If
                                        End If
                                    End Function))
    End Function

    Private Function respond_one(ByVal instance As herald) As event_comb
        assert(Not instance Is Nothing)
        assert(Not e Is Nothing)
        Dim ec As event_comb = Nothing
        Dim i As pointer(Of command) = Nothing
        Dim o As command = Nothing
        Return New event_comb(Function() As Boolean
                                  ec = instance.sense(pending_request_timeout_ms)
                                  Return waitfor(ec) AndAlso
                                         goto_next()
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      i = New pointer(Of command)()
                                      ec = instance.receive(i)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      o = New command()
                                      ec = execute(+i, o)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  If ec.end_result() Then
                                      assert(Not o.empty())
                                      ec = instance.send(o)
                                      Return waitfor(ec) AndAlso
                                             goto_next()
                                  Else
                                      Return False
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         If(continuous AndAlso
                                            Not expired(),
                                            goto_begin(),
                                            goto_end())
                              End Function)
    End Function

    Private Function respond_one() As event_comb
        Return respond_one(instance)
    End Function

    Private Function execute(ByVal i As command, ByVal o As command) As event_comb
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  If o Is Nothing OrElse
                                     Not valid() Then
                                      Return False
                                  Else
                                      If i Is Nothing OrElse
                                         i.empty() Then
                                          o.attach(constants.response.invalid_request)
                                          Return goto_end()
                                      ElseIf i.action_is(constants.action.ping) Then
                                          o.attach(constants.response.success)
                                          Return goto_end()
                                      Else
                                          ec = e.execute(i, o)
                                          Return waitfor(ec) AndAlso
                                                 goto_next()
                                      End If
                                  End If
                              End Function,
                              Function() As Boolean
                                  If Not o.has_action() Then
                                      o.attach(If(ec.end_result(),
                                                  constants.response.success,
                                                  constants.response.failure))
                                  End If
                                  Return goto_end()
                              End Function)
    End Function

    Private Shared Function create(ByVal v As var,
                                   ByVal e As executor,
                                   ByVal s As Func(Of Boolean),
                                   ByVal o As pointer(Of responder)) As event_comb
        Dim ec As event_comb = Nothing
        Return New event_comb(Function() As Boolean
                                  If v Is Nothing Then
                                      Return False
                                  Else
                                      Dim pending_request_timeout_ms As Int64 = 0
                                      pending_request_timeout_ms =
                                          v("pending-request-timeout-ms").to_int64(default_value:=npos)
                                      Dim name As String = Nothing
                                      If v.value("name", name) Then
                                          Return eva(o,
                                                     New responder(name, pending_request_timeout_ms, e, s)) AndAlso
                                                 goto_end()
                                      Else
                                          ec = secondary_resolve(
                                                    v,
                                                    constants.herald_secondary_type_name,
                                                    Function(i As new_device_event_isync_device_pool(Of herald),
                                                             ByRef r As responder) As Boolean
                                                        r = New responder(i,
                                                                          pending_request_timeout_ms,
                                                                          e,
                                                                          s)
                                                        Return True
                                                    End Function,
                                                    o)
                                          Return waitfor(ec) AndAlso
                                                 goto_next()
                                      End If
                                  End If
                              End Function,
                              Function() As Boolean
                                  Return ec.end_result() AndAlso
                                         goto_end()
                              End Function)
    End Function

    Private Shared Sub init()
        device_constructor.set_create_singleton_device_pool_binder(
            Function(x As responder, ByRef o As singleton_device_pool(Of responder)) As Boolean
                o = New singleton_device_pool(Of responder)(x)
                Return True
            End Function)
        assert(device_constructor.register(
                   parameter_allocator(Of responder, 
                                          var, 
                                          executor, 
                                          Func(Of Boolean)) _
                                      (Function(v As var,
                                                e As executor,
                                                s As Func(Of Boolean),
                                                r As pointer(Of responder)) As event_comb
                                           Return create(v, e, s, r)
                                       End Function)))
    End Sub
End Class
